<template>
  <q-page>
    <div ref="container" class="container">
      <div class="text-center">
        <div class="row q-col-gutter-md items-center">
          <div class="col-xs-12 col-md-6" v-if="!inGame">
            <div class="row q-col-gutter-sm items-center">
              <div class="col-xs-4 col-sm-2">
                执棋
              </div>
              <div class="col-xs-8 col-sm-10">
                <q-btn-toggle spread dense
                              v-model="myColor"
                              :options="[{label: '执黑', value: 'black'}, {label: '执白', value: 'white'}]"/>
              </div>
            </div>
          </div>
          <div class="col-12">
            <q-btn class="full-width"
                   color="primary"
                   :label="inGame ? '重新开始' : '开始游戏'" @click="inGame = true, init()"/>
          </div>
          <div class="col-12" v-show="inGame || winner">
            <div class="row q-col-gutter-md justify-center">
              <div class="gt-sm col-md"/>
              <div class="col-auto">
                <div ref="canvasBorder"
                     class="relative-position"
                     style="border: 1px grey solid">
                  <canvas id="canvas"
                          ref="canvas"
                          @click="chessClick"/>
                </div>
              </div>
              <div class="col-xs-12 col-md">
                <div class="row q-col-gutter-md justify-center">
                  <div class="col-xs-6 col-sm-12">
                    <div class="text-h6">玩家 执{{ (myColor === 'white') ? '白' : '黑' }}</div>
                    <div>
                      <q-badge color="red">
                        <q-icon :name="me ? 'mdi-chevron-up' : ''"/>
                      </q-badge>
                    </div>
                  </div>
                  <div class="col-xs-6 col-sm-12">
                    <div class="text-h6">电脑 执{{ (myColor === 'white') ? '黑' : '白' }}</div>
                    <div>
                      <q-badge color="red">
                        <q-icon :name="me ? '' : 'mdi-chevron-up'"/>
                      </q-badge>
                    </div>
                    <div v-if="compLastUsed">
                      电脑上一步思考了{{ compLastUsed }}毫秒
                    </div>
                  </div>
                </div>
              </div>
            </div>
          </div>
        </div>
      </div>
    </div>
  </q-page>
</template>

<script>
export default {
  data() {
    return {
      inGame: false,
      chess: {},
      context: {},
      chessBoard: [], // 记录是否走过
      me: true,
      myColor: 'black',
      count: 0, // 所有赢法数量
      wins: [], // 赢法数组
      myWin: [], // 我方赢法的统计数组
      computerWin: [], // 计算机赢法的统计数组
      over: false,
      winner: null,
      firstStep: false,
      compLastUsed: null,
      containerWidth: 1200
    }
  },
  watch: {
    me: {
      handler: function (v) {
        if (!this.inGame) return;
        if (!v) this.computerAI();
      }
    },
    winner: {
      handler: function (v) {
        if (!v || v.length <= 0) return;
        const winAnim = document.createElement('div');
        winAnim.style.textAlign = 'center';
        winAnim.style.position = 'absolute';
        winAnim.style.top = '50%';
        winAnim.style.left = '50%';
        winAnim.style.transform = 'translate(-50%, -50%)';
        winAnim.style.fontFamily = 'sgm';
        winAnim.style.color = 'orange';
        winAnim.style.fontSize = `${this.width / 10}em`;
        winAnim.innerHTML = `${(v === 'computer') ? '电脑' : '玩家'}胜利！`;
        winAnim.unselectable = 'on';
        winAnim.onselectstart = () => {
          return false;
        };
        winAnim.style['-moz-user-select'] = 'none';
        this.$refs.canvasBorder.appendChild(winAnim);
        let waitCount = 0, opacity = 1, step = 0.02;
        const winAnimTimer = setInterval(() => {
          if (waitCount < 60) {
            waitCount++;
            return;
          }
          opacity -= step;
          winAnim.style.opacity = `${opacity}`;
          if (opacity <= 0) {
            this.$refs.canvasBorder.removeChild(winAnim);
            clearInterval(winAnimTimer);
          }
        }, 20);
      }
    }
  },
  computed: {
    border: {
      get() {
        const computedSize = this.containerWidth * 0.95 / (15 + 1) / 2
        return (computedSize > 20) ? 20 : computedSize;
      }
    },
    width: {
      get() {
        return this.border * 2;
      }
    }
  },
  mounted() {
    window.onresize = this.onReSize;
    this.containerWidth = this.$refs.container.clientWidth;
  },
  methods: {
    // 初始化
    init() {
      this.chess = this.$refs.canvas
      this.context = this.chess.getContext("2d")
      this.drawChessBoard()
      this.fillArray()
      const playerFirst = (this.myColor === 'black') ? true : false;
      this.firstStep = true;
      this.compLastUsed = null;
      this.winner = null;
      if (playerFirst) this.me = true;
      else {
        if (this.me) this.me = false;
        else this.computerAI();
      }
    },
    // 填充数组
    fillArray() {
      this.count = 0;
      this.chessBoard = [];
      this.myWin = [];
      this.computerWin = [];
      this.wins = [];
      // 是否走过
      for (let i = 0; i < 15; i++) {
        this.chessBoard[i] = []
        for (let j = 0; j < 15; j++) {
          this.chessBoard[i][j] = 0
        }
      }
      // 赢法数组
      for (let i = 0; i < 15; i++) {
        this.wins[i] = []
        for (let j = 0; j < 15; j++) {
          this.wins[i][j] = []
        }
      }
      // 横
      for (let i = 0; i < 15; i++) {
        for (let j = 0; j < 11; j++) {
          for (let k = 0; k < 5; k++) {
            this.wins[i][j + k][this.count] = true
          }
          this.count++
        }
      }
      // 竖
      for (let i = 0; i < 15; i++) {
        for (let j = 0; j < 11; j++) {
          for (let k = 0; k < 5; k++) {
            this.wins[j + k][i][this.count] = true
          }
          this.count++
        }
      }
      // 斜
      for (let i = 0; i < 11; i++) {
        for (let j = 0; j < 11; j++) {
          for (let k = 0; k < 5; k++) {
            this.wins[i + k][j + k][this.count] = true
          }
          this.count++
        }
      }
      // 反斜
      for (let i = 0; i < 11; i++) {
        for (let j = 14; j > 3; j--) {
          for (let k = 0; k < 5; k++) {
            this.wins[i + k][j - k][this.count] = true
          }
          this.count++
        }
      }
      // 赢法的统计数组
      for (let i = 0; i < this.count; i++) {
        this.myWin[i] = 0
        this.computerWin[i] = 0
      }
    },
    // 绘制棋盘
    drawChessBoard() {
      const {context} = this
      context.strokeStyle = '#bfbfbf'
      this.$refs.canvas.setAttribute("width", `${this.border * 2 + 14 * this.width}px`)
      this.$refs.canvas.setAttribute("height", `${this.border * 2 + 14 * this.width}px`)
      for (let i = 0; i < 15; i++) {
        context.moveTo(this.border + i * this.width, this.border)
        context.lineTo(this.border + i * this.width, this.border + 14 * this.width)
        context.stroke()
        context.moveTo(this.border, this.border + i * this.width)
        context.lineTo(this.border + 14 * this.width, this.border + i * this.width)
        context.stroke()
      }
    },
    // 落子实现
    onStep(x, y, me) {
      this.firstStep = false;
      const {context} = this
      context.beginPath()
      context.arc(this.border + x * this.width, this.border + y * this.width, this.width / 2 - 2, 0, 2 * Math.PI)
      context.closePath()
      const gradient = context.createRadialGradient(this.border + x * this.width + 2, this.border + y * this.width - 2, 13, this.border + x * this.width + 2, this.border + y * this.width - 2, 0)
      const color = {
        white: {
          from: '#d1d1d1',
          to: '#f9f9f9'
        },
        black: {
          from: '#0a0a0a',
          to: '#636766'
        }
      };
      let currentColor;
      if (me) currentColor = this.myColor;
      else currentColor = (this.myColor === 'black') ? 'white' : 'black';
      gradient.addColorStop(0, color[currentColor].from);
      gradient.addColorStop(1, color[currentColor].to);
      context.fillStyle = gradient
      context.fill()
    },
    onReSize() {
      this.containerWidth = this.$refs.container.clientWidth;
      if (this.inGame || this.winner) {
        this.drawChessBoard();
        for (let x = 0; x < 15; x++)
          for (let y = 0; y < 15; y++) {
            if (this.chessBoard[x][y] > 0) this.onStep(x, y, this.chessBoard[x][y] === 1);
          }
      }
    },
    // 我方落子
    chessClick(e) {
      if (!this.inGame) {
        return
      }
      if (!this.me) {
        return
      }
      const ox = e.offsetX
      const oy = e.offsetY
      const x = Math.floor(ox / this.width)
      const y = Math.floor(oy / this.width)

      if (this.chessBoard[x][y] === 0) {
        this.chessBoard[x][y] = 1
        this.onStep(x, y, this.me)

        // 统计赢法
        for (let k = 0; k < this.count; k++) {
          if (this.wins[x][y][k]) {
            this.myWin[k]++
            this.computerWin[k] = 6
            if (this.myWin[k] === 5) {
              this.winner = 'player';
              this.inGame = false;
            }
          }
        }
        if (this.inGame) this.me = false;
      }
    },
    // 计算机落子
    computerAI() {
      const startTime = new Date();

      const myScore = []
      const computerScore = []
      let max = 0 // 最大分数
      let u = 0, v = 0 // 所在坐标

      if (this.firstStep) {
        u = 7, v = 7;
      } else {
        for (let i = 0; i < 15; i++) {
          myScore[i] = []
          computerScore[i] = []
          for (let j = 0; j < 15; j++) {
            myScore[i][j] = 0
            computerScore[i][j] = 0
          }
        }
        // 遍历棋盘
        for (let i = 0; i < 15; i++) {
          for (let j = 0; j < 15; j++) {
            if (this.chessBoard[i][j] === 0) {
              // 遍历所有赢法
              for (let k = 0; k < this.count; k++) {
                if (this.wins[i][j][k]) {
                  // 我方
                  if (this.myWin[k] === 1) {
                    myScore[i][j] += 200
                  } else if (this.myWin[k] === 2) {
                    myScore[i][j] += 400
                  } else if (this.myWin[k] === 3) {
                    myScore[i][j] += 2000
                  } else if (this.myWin[k] === 4) {
                    myScore[i][j] += 10000
                  }
                  // 计算机
                  if (this.computerWin[k] === 1) {
                    computerScore[i][j] += 220
                  } else if (this.computerWin[k] === 2) {
                    computerScore[i][j] += 420
                  } else if (this.computerWin[k] === 3) {
                    computerScore[i][j] += 2100
                  } else if (this.computerWin[k] === 4) {
                    computerScore[i][j] += 20000
                  }
                }
              }
              // 比较分数
              if (myScore[i][j] > max) {
                max = myScore[i][j]
                u = i
                v = j
              } else if (myScore[i][j] === max) {
                if (computerScore[i][j] > computerScore[u][v]) {
                  u = i
                  v = j
                }
              }
              if (computerScore[i][j] > max) {
                max = computerScore[i][j]
                u = i
                v = j
              } else if (computerScore[i][j] === max) {
                if (myScore[i][j] > myScore[u][v]) {
                  u = i
                  v = j
                }
              }
            }
          }
        }
      }
      this.onStep(u, v, false)
      this.chessBoard[u][v] = 2
      // 统计赢法
      for (let k = 0; k < this.count; k++) {
        if (this.wins[u][v][k]) {
          this.computerWin[k]++
          this.myWin[k] = 6
          if (this.computerWin[k] === 5) {
            this.winner = 'computer';
            this.inGame = false
          }
        }
      }
      if (this.inGame) this.me = true;

      const endTime = new Date();
      this.compLastUsed = endTime - startTime;
      console.log(this.compLastUsed);
    }
  }
}
</script>

<style>
</style>
